﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Lucene.Linq.Mapping;
using Lucene.Linq.VSTests.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Lucene.Linq.VSTests
{
    [TestClass]
    public class TestIndexSet
    {
        #region Generators

        private static readonly Random random = new Random();

        private static string RepeatWord(string word, int num)
        {
            var words = new string[num];

            for (int i = 0; i < words.Length; i++)
                words[i] = word;

            return String.Join(" ", words);
        }

        public static IEnumerable<Item> GetRandom(int num)
        {
            int i = 0;

            while (num > i)
            {
                yield return
                    new Item
                        {
                            Name = RepeatWord("thing", random.Next(10))
                        };

                i++;
            }
        }

        #endregion

        #region Fake Indexables

        #region Nested type: AnotherItem

        [Document]
        public class AnotherItem : IHit
        {
            [Field(FieldIndex.Tokenized, FieldStore.Yes, IsKey = true, IsDefault = true)]
            public string Name { get; set; }

            #region IHit Members

            public int DocumentId { get; set; }
            public float Relevance { get; set; }

            #endregion
        }

        #endregion

        #region Nested type: Item

        [Document]
        public class Item : IHit
        {
            [Field(FieldIndex.Tokenized, FieldStore.Yes, IsKey = true, IsDefault = true)]
            public string Name { get; set; }

            #region IHit Members

            public int DocumentId { get; set; }
            public float Relevance { get; set; }

            #endregion
        }

        #endregion

        #region Nested type: NotAnItem

        public class NotAnItem
        {
        }

        #endregion

        #endregion

        #region Ctors

        [TestMethod, ExpectedException(typeof(ArgumentNullException))]
        public void Construct_throws_with_null_path_or_directory()
        {
            var iset1 = new IndexSet((DirectoryInfo)null);

            var iset2 = new IndexSet((string)null);

            var iset3 = new IndexSet("");
        }

        [TestMethod]
        public void Construct()
        {
            var isetPath = new IndexSet(Path.Combine(Environment.CurrentDirectory, random.String(20)));
            var dir = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, random.String(20)));
            var isetDir = new IndexSet(dir);
            var isetRam = new IndexSet();
        }

        #endregion

        #region Add/All

        [TestMethod]
        public void Add_should_add_an_index()
        {
            var iset = new IndexSet();
            iset.Add<Item>();
            iset.Add(typeof(AnotherItem));

            Assert.AreEqual(iset.Count, 2);

            Assert.IsTrue(
                iset.All().Count(i => i.Name == "Item") > 0
                );

            Assert.IsTrue(
                iset.All().Count(i => i.Name == "AnotherItem") > 0
                );
        }

        [TestMethod, ExpectedException(typeof(ArgumentException))]
        public void Add_throws_if_type_does_not_contain_Document_attribute()
        {
            var iset = new IndexSet();
            iset.Add<NotAnItem>();
        }

        #endregion

        #region Remove

        [TestMethod]
        public void Remove_should_delete_the_index()
        {
            var iset = new IndexSet();
            iset.Add<Item>();
            iset.Add(typeof(AnotherItem));

            Assert.AreEqual(iset.Count, 2);

            Assert.IsTrue(
                iset.All().Count(i => i.Name == "Item") > 0
                );

            Assert.IsTrue(
                iset.All().Count(i => i.Name == "AnotherItem") > 0
                );

            iset.Remove<Item>();
            Assert.AreEqual(iset.Count, 1);
            iset.Remove(typeof(AnotherItem));
            Assert.AreEqual(iset.Count, 0);
            Assert.IsTrue(
                iset.All().Count(i => i.Name == "AnotherItem") == 0
                );
            Assert.IsTrue(
                iset.All().Count(i => i.Name == "Item") == 0
                );
        }

        [TestMethod, ExpectedException(typeof(ArgumentException))]
        public void Remove_should_throw_on_non_existing_index()
        {
            var iset = new IndexSet();
            iset.Add<Item>();
            Assert.AreEqual(iset.Count, 1);

            iset.Remove<AnotherItem>();
            iset.Remove(typeof(AnotherItem));
        }

        #endregion

        #region Get/Indexer

        [TestMethod]
        public void Get_returns_index()
        {
            var iset = new IndexSet();
            iset.Add<Item>();
            Assert.AreEqual(iset.Count, 1);

            IIndex<Item> idx = iset.Get<Item>();
            Assert.IsNotNull(idx);

            IIndex idx2 = iset.Get(typeof(Item));
            Assert.IsNotNull(idx2);

            IIndex idx3 = iset[typeof(Item)];
            Assert.IsNotNull(idx3);
        }

        [TestMethod, ExpectedException(typeof(ArgumentException))]
        public void Get_throws_if_index_does_not_exist()
        {
            var iset = new IndexSet();
            iset.Add<Item>();
            Assert.AreEqual(iset.Count, 1);
            iset.Get<AnotherItem>();
            iset.Get(typeof(AnotherItem));
            IIndex idx = iset[typeof(AnotherItem)];
        }

        #endregion
    }
}